// 5_函数
// 5.1、函数类型
// 5.1.1、为函数定义类型
// 第一种 定义函数类型
function add(x: number, y: number): number {
  return x + y;
}
// 第二种 定义函数类型
let myAdd = function (x: number, y: number): number {
  return x + y;
};
// 5.1.2、书写完整函数类型 函数的完整类型
let myAdd1: (x: number, y: number) => number = function (x: number, y: number): number {
  return x + y;
};
// 5.1.3、推断类型
// 如果你在赋值语句的一边指定了类型但是另一边没有类型的话，TypeScript编译器会自动识别出类型
// myAdd2具有完整的函数类型
let myAdd2 = function (x: number, y: number): number {
  return x + y;
};
// 参数“x”和“y”具有数字类型。
let myAdd3: (baseValue: number, increment: number) => number = function (x, y) {
  return x + y;
};
// 5.2、可选参数和默认参数
// TypeScript里的每个函数参数都是必须的。 这不是指不能传递 null或undefined作为参数，而是说编译器检查用户是否为每个参数都传入了值。
// 5.2.1、默认参数
export function buildName(firstName: string, lastName: string): string {
  return firstName + lastName;
}
// let result1 = buildName('Bob'); // 应有 2 个参数，但获得 1 个。
// let result2 = buildName('Bob', 'Adams', 'Sr.'); //应有 2 个参数，但获得 3 个。
let result3 = buildName('Bob', 'Adams');
console.log(result3);
// 5.2.2、可选参数 在参数名旁使用 ?实现可选参数的功能
export function buildName1(firstName: string, lastName?: string) {
  if (lastName) {
    return firstName + lastName;
  } else {
    return firstName;
  }
}
// 5.2.3、默认值参数
function buildName2(firstName = 'Will', lastName: string) {
  return firstName + ' ' + lastName;
}

// let result4 = buildName2('Bob'); // error, too few parameters
// let result5 = buildName2('Bob', 'Adams', 'Sr.'); // error, too many parameters
let result6 = buildName2('Bob', 'Adams'); // okay and returns "Bob Adams"
let result7 = buildName2(undefined, 'Adams'); // okay and returns "Will Adams"
// 5.3、剩余参数
function buildName3(firstName: string, ...restOfName: string[]) {
  return firstName + '' + restOfName.join(' ');
}
let employeeName = buildName3('Joseph', 'Samuel', 'Lucas', 'MacKinzie');
console.log(employeeName);

function buildName4(firstName: string, ...restOfName: string[]) {
  return firstName + ' ' + restOfName.join(' ');
}
// (fname: string, ...rest: string[]) => string：指定 buildNameFun 的类型。它是一个函数类型，接受一个 string 类型的参数 fname 和一个可变数量的 string 类型参数 rest，返回一个 string 类型的值。
let buildNameFun: (fname: string, ...rest: string[]) => string = buildName4;
// 5.3.1、this参数
let deck = {
  suits: ['hearts', 'spades', 'clubs', 'diamonds'],
  cards: Array(52),
  createCardPicker: function () {
    // return function () { 这么写不会弹出弹窗，因为this指向的是window
    //   let pickedCard = Math.floor(Math.random() * 52);
    //   let pickedSuit = Math.floor(pickedCard / 13);
    //   return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
    // };
    return () => {
      // 箭头函数能保存函数创建时的 this值，而不是调用时的值
      let pickedCard = Math.floor(Math.random() * 52);
      let pickedSuit = Math.floor(pickedCard / 13);
      return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
    };
  }
};

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

alert('card: ' + pickedCard.card + ' of ' + pickedCard.suit);
// 5.3.2、this参数在回调函数里
interface UIElement {
  addClickListener(onclick: (this: void, e: Event) => void): void;
}
